package org.xx.armory.swing.builders;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;

public class GridLayoutModelBuilder
        extends LayoutModelBuilder<GridLayoutModel> {
    private final Logger logger = LoggerFactory.getLogger(GridLayoutModelBuilder.class);

    private final List<ColumnModel> columns;
    private final List<RowModel> rows;
    private int margin;

    public GridLayoutModelBuilder() {
        this.margin = 2;
        this.columns = new ArrayList<>();
        this.rows = new ArrayList<>();
    }

    public final GridLayoutModelBuilder setMargin(
            int margin
    ) {
        this.margin = margin;
        return this;
    }

    public final GridLayoutModelBuilder addColumn(
            ColumnModel column
    ) {
        this.columns.add(column);
        return this;
    }

    public final GridLayoutModelBuilder addRow(
            RowModel row
    ) {
        this.rows.add(row);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public GridLayoutModel build() {
        // 通过每个单元格的rowSpan和columnSpan来计算它们的座标。

        // 首先创建一个位矩阵，表示每个位置的占用情况。
        final BitSet[] matrix = new BitSet[this.rows.size()];
        for (int i = 0; i < this.rows.size(); ++i) {
            matrix[i] = new BitSet(this.columns.size());
        }

        // 然后扫描每个单元格。
        int i, j; // 当前列号和行号。
        i = 0;
        for (final RowModel row : this.rows) {
            final List<CellModel> newCells = new ArrayList<>(row.getCells().size());

            j = 0;
            for (final CellModel cell : row.getCells()) {
                // 判断当前位置是否已经越界。
                if (j >= this.columns.size()) {
                    logger.warn("cell out of range: i = {}, j = {}", i, j);
                    continue;
                }
                // 判断当前位置是否已被占用。
                if (matrix[i].get(j)) {
                    // 如果当前位置已被占用，那么将列号推进到最近未被占用的列。
                    j = matrix[i].nextClearBit(j);
                    logger.trace("skip to {}", j);

                    // 再次检查当前位置是否已越界。
                    if (j >= this.columns.size()) {
                        logger.warn("cell out of range: i = {}, j = {}", i, j);
                        continue;
                    }
                }

                final ColumnModel column = this.columns.get(j);

                int rowSpan = cell.getRowSpan();
                int columnSpan = cell.getColumnSpan();
                ModelAlignment horizontalAlignment = cell.getHorizontalAlignment();
                if (horizontalAlignment == ModelAlignment.NONE) {
                    horizontalAlignment = column.getAlignment();
                }
                ModelAlignment verticalAlignment = cell.getVerticalAlignment();
                if (verticalAlignment == ModelAlignment.NONE) {
                    verticalAlignment = row.getAlignment();
                }
                int horizontalStretch = column.getStretch();
                int verticalStretch = row.getStretch();

                int margin = cell.getMargin();
                if (margin < 0) {
                    margin = this.margin;
                }

                final CellModel newCell = new CellModel(cell.getLineInfo(), i, j,
                                                        rowSpan, columnSpan,
                                                        horizontalAlignment, verticalAlignment,
                                                        horizontalStretch, verticalStretch,
                                                        margin,
                                                        cell.getChild());
                logger.trace("create cell: i = {}, j = {}, rowSpan = {}, columnSpan = {}", i, j, rowSpan, columnSpan);
                newCells.add(newCell);

                // 根据rowSpan和columnSpan计算该单元格的实际占用位置。
                if (rowSpan == 1 && columnSpan == 1) {
                    // 单元格不存在扩展，那么只占用一个位置。
                    matrix[i].set(j);
                } else {
                    // 否则将这个矩形块都设置为已占用。
                    for (int m = 0; m < rowSpan; ++m) {
                        matrix[i + m].set(j, j + columnSpan);
                    }
                }

                j += columnSpan;
            }

            this.rows.set(i, new RowModel(row.getLineInfo(), row.getStretch(), row.getAlignment(), newCells));

            ++i;
        }

        return new GridLayoutModel(
                getLineInfo(),
                getName(),
                isVisible(),
                getTitle(),
                getBorder(),
                this.margin,
                this.columns,
                this.rows);
    }
}
